package pers.whj.handler;

public class MessageQueue {
    private final Object LOCK = new Object();
    private Message mMessages = null;
    private int mNextPollTimeoutMillis = -1;

    public boolean enqueueMessage(Message msg) {
        synchronized (LOCK) {
            Message tmp = mMessages;
            if (tmp == null || msg.when == 0 || msg.when < tmp.when) {
                msg.next = tmp;
                mMessages = msg;
            } else {
                Message pre;
                do {
                    pre = tmp;
                    tmp = tmp.next;
                } while (tmp != null && msg.when >= tmp.when);
                msg.next = tmp;
                pre.next = msg;
            }
            mNextPollTimeoutMillis = 0;
            LOCK.notify();
        }
        return true;
    }

    public Message next() {
        while (true) {
            synchronized (LOCK) {
                try {
                    if (mNextPollTimeoutMillis < 0) {
                        LOCK.wait();
                    } else if (mNextPollTimeoutMillis > 0) {
                        LOCK.wait(mNextPollTimeoutMillis);
                    }
                    final long now = System.currentTimeMillis();
                    Message msg = mMessages;
                    if (msg != null) {
                        if (msg.when > now) {
                            mNextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                        } else {
                            mMessages = msg.next;
                            msg.next = null;
                            if (mMessages != null) {
                                mNextPollTimeoutMillis = (int) Math.min(mMessages.when - now, Integer.MAX_VALUE);
                                mNextPollTimeoutMillis = Math.max(mNextPollTimeoutMillis, 0);
                            } else {
                                mNextPollTimeoutMillis = -1;
                            }
                        }
                        return msg;
                    } else {
                        mNextPollTimeoutMillis = -1;
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                    return null;
                }
            }
        }
    }

    public boolean hasMessages(Handler target, int what, Object obj) {
        if (target == null) {
            return false;
        }
        synchronized (LOCK) {
            Message tmp = mMessages;
            while (tmp != null) {
                if (tmp.target == target && tmp.what == what && (obj == null || tmp.obj == obj)) {
                    return true;
                }
                tmp = tmp.next;
            }
            return false;
        }
    }

    public void removeMessages(Handler target, int what, Object obj) {
        if (target == null) {
            return;
        }
        synchronized (LOCK) {
            Message tmp = mMessages;
            //remove messages at front
            while (tmp != null && tmp.target == target && tmp.what == what && (obj == null || tmp.obj == obj)) {
                mMessages = tmp.next;
                tmp.recycleUnchecked();
                tmp = mMessages;
            }
            //remove messages after front
            while (tmp != null) {
                Message nextMsg = tmp.next;
                if (nextMsg != null) {
                    if (nextMsg.target == target && nextMsg.what == what && (obj == null || nextMsg.obj == obj)) {
                        tmp.next = nextMsg.next;
                        nextMsg.recycleUnchecked();
                        continue;
                    }
                }
                tmp = nextMsg;
            }
        }
    }

    public void removeMessages(Handler target, Runnable callback, Object obj) {
        if (target == null || callback == null) {
            return;
        }
        synchronized (LOCK) {
            Message tmp = mMessages;
            //remove messages at front
            while (tmp != null && tmp.target == target && tmp.callback == callback && (obj == null || tmp.obj == obj)) {
                mMessages = tmp.next;
                tmp.recycleUnchecked();
                tmp = mMessages;
            }
            //remove messages after front
            while (tmp != null) {
                Message nextMsg = tmp.next;
                if (nextMsg != null) {
                    if (nextMsg.target == target && nextMsg.callback == callback && (obj == null || nextMsg.obj == obj)) {
                        tmp.next = nextMsg.next;
                        nextMsg.recycleUnchecked();
                        continue;
                    }
                }
                tmp = nextMsg;
            }
        }
    }

    public void removeCallbacksAndMessages(Handler target, Object obj) {
        if (target == null) {
            return;
        }
        synchronized (LOCK) {
            Message tmp = mMessages;
            //remove messages at front
            while (tmp != null && tmp.target == target && (obj == null || tmp.obj == obj)) {
                mMessages = tmp.next;
                tmp.recycleUnchecked();
                tmp = mMessages;
            }
            //remove messages after front
            while (tmp != null) {
                Message nextMsg = tmp.next;
                if (nextMsg != null) {
                    if (nextMsg.target == target && (obj == null || nextMsg.obj == obj)) {
                        tmp.next = nextMsg.next;
                        nextMsg.recycleUnchecked();
                        continue;
                    }
                }
                tmp = nextMsg;
            }
        }
    }
}